import os

from fsspec import AbstractFileSystem, url_to_fs
from fsspec.implementations.dirfs import DirFileSystem
from fsspec.implementations.local import LocalFileSystem

Fs = AbstractFileSystem
FsLike = tuple[AbstractFileSystem, str] | AbstractFileSystem | os.PathLike | str


def resolve_fs(fs: FsLike) -> AbstractFileSystem:
    if isinstance(fs, os.PathLike | str):
        fs, path = url_to_fs(fs)
        return DirFileSystem(path, fs)
    if isinstance(fs, AbstractFileSystem):
        return fs
    if isinstance(fs, tuple) and len(fs) == 2:
        fs, path = fs
        if isinstance(fs, AbstractFileSystem) and isinstance(path, str):
            return DirFileSystem(path, fs)
    raise ValueError(f"Unsupported path type: {type(fs)}")


def resolve_parent_fs(fs: FsLike) -> tuple[AbstractFileSystem, str]:
    fs = resolve_fs(fs)
    if not isinstance(fs, DirFileSystem):
        raise TypeError(f"Cannot resolve parent of {fs}!")
    fs, path = fs.fs, fs.path

    if path == fs.root_marker or path == "":
        raise ValueError(f"Cannot load parent of root path!")

    sep_index = path.rfind(fs.sep)
    if sep_index != -1:
        parent_fs = DirFileSystem(path[:sep_index], fs)
        child_name = path[sep_index + 1:]
    else:
        parent_fs = DirFileSystem(fs.root_marker, fs)
        child_name = path

    return parent_fs, child_name


def resolve_child_fs(fs: FsLike, path: str):
    fs = resolve_fs(fs)
    if isinstance(fs, DirFileSystem):
        path = _join_path(fs.sep, fs.path, path)
        fs = fs.fs
    return DirFileSystem(path, fs)


def resolve_local_path(fs: FsLike) -> str:
    fs = resolve_fs(fs)
    if isinstance(fs, LocalFileSystem):
        return fs.root_marker
    if isinstance(fs, DirFileSystem):
        return _join_path(fs.sep, resolve_local_path(fs.fs), fs.path)
    raise TypeError(f"Expected LocalFileSystem or DirFileSystem, got {fs}")


def _join_path(sep: str, path0: str, path1: str):
    if not path0:
        return path1
    if not path1:
        return path0
    if path0.endswith(sep):
        path0 = path0[:-len(sep)]
    if path1.startswith(sep):
        path1 = path1[len(sep):]
    return path0 + sep + path1
